package com.hz.qs.service;

import cn.hutool.core.util.IdUtil;
import com.hz.qs.dao.CoreDao;
import com.hz.qs.dao.CustomerDao;
import com.hz.qs.model.YiQueues;
import com.hz.qs.model.YiSpecialProject;
import com.hz.qs.model.vo.CustomerDataVo;
import com.hz.qs.model.vo.CustomerRequest;
import com.hz.qs.utils.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.hz.qs.utils.Common.END_TIME;
import static com.hz.qs.utils.Common.START_TIME;


@Service
@Slf4j
public class CoreService {


    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private CoreDao coreDao;

    private static final String error = "ERROR";

    //特殊类型项目处理集合
    private static final List<Integer> REPLACE_LIST = Arrays.asList(45);

    /**
     * 获取签到人员全部检查项目的全部可选队列的集合
     *
     * @param customerRequest
     * @return java.util.List<com.hz.qs.model.vo.CustomerDataVo>
     * @author yejy
     * @date 2020/11/10 13:54
     */
    public LinkedList<CustomerDataVo> getCustomersAllCheckProjectQueues(CustomerRequest customerRequest) {
        LocalDate date = customerRequest.getCurrentDate();
        String firstDate = date + START_TIME;
        String endDate = date + END_TIME;
        return customerDao.getCustomerData(customerRequest, firstDate, endDate);
    }

    public LinkedList<CustomerDataVo> getCustomersCount(CustomerRequest customerRequest) {
        LocalDate date = customerRequest.getCurrentDate();
        String firstDate = date + START_TIME;
        String endDate = date + END_TIME;
        return customerDao.getCustomersCount(customerRequest, firstDate, endDate);
    }

    /**
     * 多队列多用户最优排序处理函数
     *
     * @param customerRequest
     * @return void
     * @author yejy
     * @date 2020/12/31 16:24
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized void optQueuesData2(CustomerRequest customerRequest) {
        LinkedList<String> customerIdAllList = new LinkedList<>();
        LocalDate currentDate = customerRequest.getCurrentDate();
        ConcurrentHashMap<String, Long> lastProjectTime = new ConcurrentHashMap<>(16);
        List<YiSpecialProject> specialList = coreDao.getSpecialData();
        // 获取签到表当天的客户的全部的项目和全部可用的队列数据
        LinkedList<CustomerDataVo> dataVoList = getCustomersAllCheckProjectQueues(customerRequest);
        List<String> customerIdOneList = dataVoList.stream().sorted(Comparator.comparing(CustomerDataVo::getOrderNo)).map(CustomerDataVo::getCustomerId).distinct().collect(Collectors.toList());
        LinkedList<CustomerDataVo> customersCount = getCustomersCount(customerRequest);
        int maxCount = customersCount.stream().max(Comparator.comparing(CustomerDataVo::getCount)).map(CustomerDataVo::getCount).orElse(100);
        for (int i = 0; i < maxCount; i++) {
            customerIdAllList.addAll(customerIdOneList);
        }
        for (String customerId : customerIdAllList) {
            List<YiQueues> queuesList = getYiQueuesData(customerId, currentDate);
            List<CustomerDataVo> oneCustomerList = dataVoList.stream().filter(f -> f.getCustomerId().equals(customerId)).collect(Collectors.toList());
            optSpecialData(oneCustomerList, specialList);
            // 这里过滤出这个客户没有进行分配的项目
            List<CustomerDataVo> validCustomerDetailList = checkCustomerExistQueue(queuesList, oneCustomerList);
            if (validCustomerDetailList.size() > 0) {
                // 获取实时的队列表数据，用于计算当前每个队列的预计时间
                multiQueueSortCore(customerId, lastProjectTime, currentDate, validCustomerDetailList);
            }
        }
    }


    /**
     * 多队列多用户最优排序核心函数
     *
     * @param customerId
     * @param lastProjectTime
     * @param currentDate
     * @param validCustomerDetailList
     * @return void
     * @author yejy
     * @date 2021/1/13 14:38
     */
    private void multiQueueSortCore(String customerId, ConcurrentHashMap<String, Long> lastProjectTime, LocalDate currentDate, List<CustomerDataVo> validCustomerDetailList) {
        List<String> validQueueIdList = validCustomerDetailList.stream().map(CustomerDataVo::getQueueId).distinct().collect(Collectors.toList());
        String firstDate = currentDate + START_TIME;
        String endDate = currentDate + END_TIME;
        List<YiQueues> inQueueData = coreDao.getQueueDataByQueueIds(validQueueIdList, firstDate, endDate);
        Map<String, Long> queueCount = inQueueData.stream().collect(Collectors.groupingBy(YiQueues::getQueueId, Collectors.counting()));
        addQueueForCalculateShortTime(customerId, queueCount, validQueueIdList, validCustomerDetailList, lastProjectTime);
        optSortSecond(customerId, queueCount, inQueueData, validCustomerDetailList, lastProjectTime);
    }

    /**
     * 核心函数的二次处理
     *
     * @param customerId
     * @param queueCount
     * @param inQueueData
     * @param validCustomerDetailList
     * @param lastProjectTime
     * @return void
     * @author yejy
     * @date 2021/1/15 11:17
     */
    private void optSortSecond(String customerId, Map<String, Long> queueCount, List<YiQueues> inQueueData, List<CustomerDataVo> validCustomerDetailList, ConcurrentHashMap<String, Long> lastProjectTime) {
        double orderNo = 0.000d;
        long LastTime = Optional.ofNullable(lastProjectTime.get(customerId)).orElse(0L);
        String shortestQueueId = MapUtil.sortMapByValue(queueCount).keySet().iterator().next();
        lastProjectTime.put(customerId, Math.abs(LastTime + queueCount.get(shortestQueueId)));
        Optional<YiQueues> first = inQueueData.stream().filter(f -> shortestQueueId.equals(f.getQueueId())).max(Comparator.comparing(YiQueues::getOrderNo));
        if (first.isPresent()) {
            orderNo = first.map(YiQueues::getOrderNo).orElse(9999.000d);
        }
        Optional<CustomerDataVo> dataVo = validCustomerDetailList.stream().filter(f -> shortestQueueId.equals(f.getQueueId())).findAny();
        optProjectIdOrQueueIdSame(dataVo, validCustomerDetailList, orderNo + 1.000d);
    }

    /**
     * 用于添加排序队列的处理函数
     *
     * @param customerId
     * @param queueCount
     * @param validQueueIdList
     * @param validCustomerDetailList
     * @param lastProjectTime
     * @return void
     * @author yejy
     * @date 2021/1/13 14:40
     */
    private void addQueueForCalculateShortTime(String customerId, Map<String, Long> queueCount, List<String> validQueueIdList, List<CustomerDataVo> validCustomerDetailList, ConcurrentHashMap<String, Long> lastProjectTime) {
        long LastTime = Optional.ofNullable(lastProjectTime.get(customerId)).map(Math::abs).orElse(0L);
        for (String queueId : validQueueIdList) {
            if (!queueCount.containsKey(queueId)) {
                queueCount.put(queueId, -1L);
            }
        }
        for (Map.Entry<String, Long> map : queueCount.entrySet()) {
            for (CustomerDataVo dataVo : validCustomerDetailList) {
                if (dataVo.getQueueId().equals(map.getKey())) {
                    map.setValue(map.getValue() * (Optional.ofNullable(dataVo.getWasteTime()).orElse(1L)) + LastTime);
                    break;
                }
            }
        }
    }

    /**
     * 同一类型的项目在同一队列，排序相同函数
     *
     * @param dataVo
     * @param validCustomerList
     * @param orderNo
     * @return void
     * @author yejy
     * @date 2020/12/31 16:26
     */
    private void optProjectIdOrQueueIdSame(Optional<CustomerDataVo> dataVo, List<CustomerDataVo> validCustomerList, Double orderNo) {
        String queueId = dataVo.map(CustomerDataVo::getQueueId).orElse(error);
        Integer projectTypeId = dataVo.map(CustomerDataVo::getProjectTypeId).orElse(-1);
        List<CustomerDataVo> collect = validCustomerList.stream().filter(f -> projectTypeId.equals(f.getProjectTypeId()) && queueId.equals(f.getQueueId())).collect(Collectors.toList());
        if (projectTypeId > 0 && !error.equals(queueId) && collect.size() > 0) {
            collect.forEach(k -> addYiQueuesCore(Optional.ofNullable(k), orderNo));
        }
    }

    /**
     * 处理特殊函数
     *
     * @param validList
     * @param specialList
     * @return void
     * @author yejy
     * @date 2020/12/25 17:29
     */
    private void optSpecialData(List<CustomerDataVo> validList, List<YiSpecialProject> specialList) {
        List<String> projectList = specialList.stream().map(YiSpecialProject::getProjectId).collect(Collectors.toList());
        Map<Integer, List<CustomerDataVo>> listMap = validList.stream().filter(f -> REPLACE_LIST.contains(f.getProjectTypeId())).collect(Collectors.groupingBy(CustomerDataVo::getProjectTypeId));
        listMap.forEach((k, v) -> validList.removeIf(r -> projectList.contains(r.getCheckProjectId())));
    }

    /**
     * 过滤已经在队列表中出现的数据
     *
     * @param queuesList
     * @param dataVoList
     * @return java.util.List<com.hz.qs.model.vo.CustomerDataVo>
     * @author yejy
     * @date 2020/11/20 14:38
     */
    private List<CustomerDataVo> checkCustomerExistQueue(List<YiQueues> queuesList, List<CustomerDataVo> dataVoList) {
        List<String> optedCustomerList = queuesList.stream().map(m -> m.getCustomerId() + "#" + m.getProjectId()).collect(Collectors.toList());
        dataVoList.removeIf(f -> optedCustomerList.contains(f.getCustomerId() + "#" + f.getCheckProjectId()));
        return dataVoList;
    }

    /**
     * 添加核心队列数据
     *
     * @param dataVo
     * @param first
     * @return void
     * @author yejy
     * @date 2020/11/12 16:18
     */
    private void addYiQueuesCore(Optional<CustomerDataVo> dataVo, Double first) {
        YiQueues queues = new YiQueues();
        queues.setCustomerId(dataVo.map(CustomerDataVo::getCustomerId).orElse(error));
        queues.setJgsId(dataVo.map(CustomerDataVo::getJgsId).orElse(error));
        queues.setOrderNo(first);
        queues.setState(dataVo.map(CustomerDataVo::getState).orElse(-1));
        queues.setProjectId(dataVo.map(CustomerDataVo::getCheckProjectId).orElse(error));
        queues.setProjectName(dataVo.map(CustomerDataVo::getCheckProjectName).orElse(error));
        queues.setCreateTime(dataVo.map(CustomerDataVo::getOptTime).orElse(new Date()));
        queues.setQueueId(dataVo.map(CustomerDataVo::getQueueId).orElse(error));
        queues.setQueueName(dataVo.map(CustomerDataVo::getQueueName).orElse(error));
        queues.setAge(dataVo.map(CustomerDataVo::getAge).orElse(""));
        queues.setName(dataVo.map(CustomerDataVo::getName).orElse(""));
        queues.setSex(dataVo.map(CustomerDataVo::getSex).orElse(""));
        queues.setPersonId(dataVo.map(CustomerDataVo::getPersonId).orElse(""));
        queues.setPhone(dataVo.map(CustomerDataVo::getPhone).orElse(""));
        queues.setWasteTime(dataVo.map(CustomerDataVo::getWasteTime).orElse(1L));
        queues.setXmlxName(dataVo.map(CustomerDataVo::getXmlxName).orElse(""));
        queues.setId(IdUtil.fastSimpleUUID());
        if (!queues.getQueueId().equals(error)) {
            customerDao.insertQueue(queues);
        }
    }

    /**
     * 获取指定日期的队列表数据
     *
     * @param customerId
     * @param currentDate
     * @return java.util.List<com.hz.qs.model.YiQueues>
     * @author yejy
     * @date 2020/11/13 14:47
     */
    public LinkedList<YiQueues> getYiQueuesData(String customerId, LocalDate currentDate) {
        String firstDate = currentDate + START_TIME;
        String endDate = currentDate + END_TIME;
        return customerDao.getYiQueuesData(customerId, firstDate, endDate);
    }

    /**
     * 获取客户列表的检查队列集合
     *
     * @param customerRequest
     * @return java.util.LinkedList<java.lang.String>
     * @author yejy
     * @date 2020/11/17 16:16
     */
    public LinkedList<Map<String,Object>> groupByQueues(CustomerRequest customerRequest) {
        LinkedList<Map<String,Object>> linkedList = new LinkedList<>();
        String firstDate = customerRequest.getCurrentDate() + START_TIME;
        String endDate = customerRequest.getCurrentDate() + END_TIME;
        LinkedList<CustomerDataVo> qList = coreDao.groupByQueues(customerRequest, firstDate, endDate);
        Map<String, List<CustomerDataVo>> collect = qList.stream().collect(Collectors.groupingBy(CustomerDataVo::getQueueName));
        collect.forEach((k, v) -> {
            Map<String,Object> map = new HashMap<>(2);
            long count = v.stream().map(CustomerDataVo::getCheckId).distinct().count();
            map.put("name",k);
            map.put("num",count);
            linkedList.push(map);
        });
        return linkedList;
    }

    /**
     * 获取客户列表客户详细数据
     *
     * @param customerRequest
     * @return java.util.List<com.hz.qs.model.vo.CustomerDataVo>
     * @author yejy
     * @date 2020/11/17 16:17
     */
    public List<CustomerDataVo> getCustomersList(CustomerRequest customerRequest) {
        String firstDate = customerRequest.getCurrentDate() + START_TIME;
        String endDate = customerRequest.getCurrentDate() + END_TIME;
        return coreDao.groupByQueues(customerRequest, firstDate, endDate);
    }
}
